/*
*   Promise的实现机制
*  1, Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 执行器会立即执行
*  2, Promise 有三个状态 pending fulfilled rejected
*     pending -> fulfilled
*     pending -> rejected
*     一旦状态确定就不可更改
*  3, resolve 和 reject 函数是用来更改状态的
*     resolve: fulfilled
*     reject: rejected
*  4, then 指定/接收两个回调函数
*     then 方法内部做的事情就判断状态并且做下一步处理
*     成功 → 成功函数
*     失败 → 失败函数
*     每个promise对象都可以调用then方法，所以then方法是被定义在原型对象中的
*  5, then 成功失败之后，有一个参数 表示成功之后的值或者失败原因
* =======================以上是Promise类核心逻辑实现===========================
*
* =======================考虑异步的问题=======================================
*  6, 实现then方法多次调用添加多个处理函数
*  7, then 方法链式调用 → 都返回promise对象
*  8, then 方法回调函数的返回值要传递给下一个promise对象
*  9, then 返回的可以是一个值，也可以是一个promise对象，需要查看状态，如果是成功的调用resolve，如果是失败的调用reject
*  10,then 不允许 promise对象自返回
*  11,相关方法实现（all, catch, finally, resolve）
* */

// 使用常量定义三个状态
// 复用并且代码有提示
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
  // 1,Promise 就是一个类 在执行这个类的时候 需要传递一个执行器进去 new promise的时候执行器会立即执行
  constructor(executor) {
    // 执行器异常处理
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  
  // 初始化 Promise 有三个状态 pending fulfilled rejected
  status = PENDING;
  
  // 初始化fulfilled 传值
  // 初始化 rejected 原因
  // 每一个promise对象都有自己的成功失败的值/原因，所以应该定义在实例对象上
  value = undefined;
  reason = undefined;
  
  // 链式成功回调
  // 数组，储存多个回调函数
  // 实现then方法多次调用添加多个处理函数
  successCallback = [];
  failCallback = [];
  
  // 3，resolve 和 reject 函数是用来更改状态的
  // 既然要更改状态，首先要有状态，实例属性status
  // 箭头函数形式是为了让this指向class实例对象
  resolve = value => {
    // 一旦状态确定就不可更改
    if (this.status !== PENDING) return;
    this.status = FULFILLED;
    // 保存成功后的值
    this.value = value;
    // 判断成功回调是否存在
    // 在等待里添加，在成功或者失败里执行并且从数组里移除当前函数
    while (this.successCallback.length) this.successCallback.shift()();
  };
  
  // 3，resolve 和 reject 函数是用来更改状态的
  // 箭头函数形式是为了让this指向class实例对象
  reject = reason => {
    // 一旦状态确定就不可更改
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在
    // 在等待里添加，在成功或者失败里执行并且从数组里移除当前函数
    while (this.failCallback.length) this.failCallback.shift()();
  };
  
  // 每个promise对象都可以调用then方法，所以then方法是被定义在原型对象中的
  // 4, then 方法内部做的事情就判断状态，成功-》成功函数 失败-》失败函数 then方法是被定义在原型对象中的
  then (successCallback, failCallback) {
    return new MyPromise((resolve, reject) => {
      // 判断状态
      // 5, then 成功失败之后，有一个参数 表示成功之后的值或者失败原因
      if (this.status === FULFILLED) {
        // 异步
        setTimeout(() => {
          // 异常处理
          try {
            let x = successCallback(this.value);
            // 如果是普通值，直接调用resolve
            // 如果是promise对象，需要查看状态
            resolvePromise(x, resolve, reject);
          } catch (e) {
            reject(e)
          }
        }, 0);
        // 5, then 成功失败之后，有一个参数 表示成功之后的值或者失败原因
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          // 异常处理
          try {
            failCallback(this.reason);
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else {
        // pending状态下的异步处理
        // 等待状态下存储成功回调
        this.successCallback.push(() => {
          // 异步
          setTimeout(() => {
            // 异常处理
            try {
              let x = successCallback(this.value);
              resolvePromise(x, resolve, reject);
            } catch (e) {
              reject(e)
            }
          }, 0);
        });
        // 等待状态下存储失败回调
        this.failCallback.push(() => {
          setTimeout(() => {
            // 异常处理
            try {
              failCallback(this.reason);
            } catch (e) {
              reject(e)
            }
          }, 0)
        });
      }
    });
  }
  
  // finally实现
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => throw reason)
    })
  }
  
  // catch实现
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
  
  // all实现
  static all (array) {
    let result = [];
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value;
        index++;
        if (index === array.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        if (current instanceof MyPromise) {
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          addData(i, array[i])
        }
      }
    })
  }
  
  // 静态方法 Promise.resolve(value)
  static resolve(value) {
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value))
  }
}

// 6, then 方法链式调用 都返回promise对象
// 7, then 方法回调函数的返回值要传递给下一个promise对象
// 8, then 返回的可以是一个值，也可以是一个promise对象，需要查看状态，如果是成功的调用resolve，如果是失败的调用reject
function resolvePromise(promise2, x, resolve, reject) {
  // 9, then 不允许 promise对象自返回
  if (promise2 === x) {
    return reject(new TypeError('1231313131'));
  }
  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

module.exports = MyPromise;